/**
 * A Javascript Library to extend standard HTML DOM interface.
 * @module base
 * @namespace
 */

var G = null;

if (typeof(console) == "undefined") {
	console = function() { };
	console.log = function(){};
	console.err = function(){};
}
(function(){
	var __Document = null;
	var __DomElements = {}; 
	var __UUID = 0;

	/**
	 * <p>The global namespace.</p>
	 * <p>It's also a factory to create an instance of G.DOM.Element.</p>
	 * <p>Use following code to create an Instance of DomElement:<br/>
	 * var obj = G(selector)<br/>
	 * There 3 kind of selector supported:
	 *	string: as the Id of the element<br/>
	 *	document: the document of DOM.<br/>
	 *	DOM element: the Dom element. An id will be assigned to the 
	 *	element if it has not a id.
	 * </p>
	 * @class G
	 * @param selector {string} the id of the dom element
	 * @param selector {document} 
	 * @param selector {dom element} 
	 */
	G = function(selector) {
		if (selector == document) {
			if (!__Document) {
				__Document = new G.DOM.Element(document);
			}
			return __Document;
		}

		if (typeof(selector) == 'string') {
			if (selector in __DomElements) {
				return __DomElements[selector];
			} else {
				var em = document.getElementById(selector);
				if (em) {
					var g = new G.DOM.Element(em);
					if (typeof(em.id) != 'string') {
						em.id = G.get_uuid('dom_');
					}
					__DomElements[em.id] = g;
					return g;
				} else {
					throw new Error("Can't find the DOM Element with id=" + selector);
				}
			}
		} else {
			return new G.DOM.Element(selector);
		}
	}

	/**
	 * Create a new Namespace with given name for G
	 * @static
	 * @method Namespace
	 * @param name {string}
	 */
	G.Namespace = function(name) {
		G[name] = G[name] || {};
	}

	G.Base = {};


	/**
	 * the uuid is a number keep increasing on every time the get_uuid was called.
	 * @static
	 * @method get_uuid
	 * @param prefix {string}
	 */
	G.get_uuid = function(prefix)
	{
		__UUID += 1;
		return prefix + __UUID;
	}

	/**
	 * This function define a new Class in namespace of G. You create a class 
	 * in the given namespace, with given name and several base classes. 
	 * The following example defined a class G.Father as base class and a 
	 * class G.Child inherited from G.Father in the name space of Utils
	 * <p> <code>
	 *	Father = G.GClass('Utils', 'Father');<br/>
	 *	Father.prototype.__init = function(id) {<br/>
	 *		this.id = id;<br/>
	 *	}<br/>
	 * <br/>
	 *	Child = G.GClass('Utils', 'Child', G.Utils.Father);<br/>
	 *	Child.prototype.__init = function(id, name) {<br/>
	 *		this.__Utils__Father__init(id);<br/>
	 *		this.name = name;<br/>
	 *	}<br/>
	 * </code></p>
	 * @static
	 * @method GClass
	 * @return {function}
	 */
	G.GClass = function(namespace, name) {
		var C = function() {
			this.__init.apply(this, arguments);
		}

		for (var i = 2; i < arguments.length; i++) {
			var base = function(){};
			base.prototype = arguments[i].prototype;

			C.prototype = new base();

			var baseinit = "__" + C.prototype.__className + "__init";
			C.prototype[baseinit] = C.prototype.__init;
		}
		C.prototype.__className = namespace + "__" + name;

		G[namespace] = G[namespace] || {};
		G[namespace][name] = C;
		return C;
	}


	/**
	 * An interface for general G object that can fire events and 
	 * bind event handler. 
	 *
	 * @class EventListener
	 *
	 */
	EventListener = G.GClass('Base', 'EventListener');
	EventListener.prototype.__init = function() {
		this.handlerList = [];
	}

	/**
	 * <P>Bind an Object and it's function to a event handler. The event handler is
	 * the combination of an Object and an function.</P>
	 * <P>An EventListener maintain a list of event handlers for every different 
	 * event. If the event was binded to a handler at the first time, listen will
	 * be called. If the last event handler was unbinded, the unlisten will be called
	 * </P>
	 *
	 * @method bind
	 * @param evname {string} the name of the event
	 * @param func {function} which function will be called when the event occur.
	 * @param obj {object} what is the "this" point to in func.
	 */
	G.Base.EventListener.prototype.bind = function(evname, func, obj) 
	{

		if (typeof(obj) == "undefined" || !obj) {
			var obj = this;
		}
		var hdlList = null;
		var dat = {'func': func, 'obj': obj};

		if (evname in this.handlerList) {
			hdlList = this.handlerList[evname];
			var n = hdlList.search(dat, function(dat1, dat2) {
				return(dat1.obj == dat2.obj && dat1.func == dat2.func);
			});
			if (n)	return;
		} else {
			hdlList = new G.Utils.List();
			this.handlerList[evname] = hdlList;
		}
		var n = new G.Utils.ListNode(dat);
		hdlList.append(n);

		if (hdlList.has_only_one()) {
			this.__listen(evname);
		}
	}

	/**
	 * Unbind an event handler for the event with given name. If all the 
	 * handler for this event was removed, the unlisten(evname) will be called.
	 *
	 * @method unbind
	 * @param evname {string} name of the event
	 * @param func {function} function of the event handler
	 * @param obj {object} object of the event handler
	 */
	G.Base.EventListener.prototype.unbind = function(evname, func, obj) 
	{
		var dat = {'func': func, 'obj': obj};
		if (evname in this.handlerList) {
			hdlList = this.handlerList[evname];
			n = hdlList.search(dat, function(dat1, dat2) {
				return(dat1.obj == dat2.obj && dat1.func == dat2.func);
			});
			if (n) {
				hdlList.remove(n);
			}
		}

		if (hdlList.is_empty()) {
			this.__unlisten(evname);
		}
	}

	/**
	 * A class inherited form G.Base.EventListener should override this 
	 * If something need to do before start monitor on some events.
	 * @method __listen
	 */
	G.Base.EventListener.prototype.__listen = function() { }

	/**
	 * A class inherited form G.Base.EventListener should override this 
	 * If something need to do before stop monitor on some events.
	 * @method __unlisten
	 */
	G.Base.EventListener.prototype.__unlisten = function() { }


	/**
	 * Fire a event, and call all the handlers binded to it.
	 *
	 * @method fire
	 */
	G.Base.EventListener.prototype.fire = function(evname, ev) {
		if (evname in this.handlerList) {
			var hdlList = this.handlerList[evname];
			for (var n = hdlList.head; n != null; n = n.next) {
				n.data.func.call(n.data.obj, ev);
			}
		}
	}
})();


